
#include <BPainter>
#include <BStyle>
#include <BImage>
#include <BFont>
#include <BEvent>
#include <BMouseEvent>
#include <BKeyEvent>
#include <BStyle>

#include <BButton>
#include "member_BButton.h"
#include "member_BAction.h"
#include "member_BShortcut.h"
#include "member_BStyle.h"

using namespace BWE;

#define member					(*(member_BButton*)_ptr)
#define member_allocate()		_ptr = new member_BButton(this)
#define member_release()		delete (member_BButton*)_ptr

BButton::BButton(BWidget* parent) : BWidget(parent)
{
	member_allocate();
}
BButton::BButton(Form form, BWidget* parent) : BWidget(parent)
{
	member_allocate();
	member.alignment.ia_form = form;
}
BButton::BButton(Form form, const BString& text, BWidget* parent) : BWidget(parent)
{
	member_allocate();
	member.text = text;
	member.alignment.ia_form = form;
}
BButton::BButton(const BString& text, BWidget* parent) : BWidget(parent)
{
	member_allocate();
	member.text = text;
}
BButton::BButton(Graph graph, const BString& text, BWidget* parent) : BWidget(parent)
{
	member_allocate();
	member.graph = graph;
	member.text = text;
}
BButton::BButton(BIcon* icon, const BString& text, BWidget* parent) : BWidget(parent)
{
	member_allocate();
	setIcon(icon);
	member.text = text;
}
BButton::BButton(BImage* image, const BString& text, BWidget* parent) : BWidget(parent)
{
	member_allocate();
	member.image = image;
	member.text = text;
}
BButton::BButton(Graph graph, BWidget* parent) : BWidget(parent)
{
	member_allocate();
	member.graph = graph;
}
BButton::BButton(Graph graph, const BColor& color, BWidget* parent) : BWidget(parent)
{
	member_allocate();
	member.graph = graph;
	member.color = color;
}
BButton::BButton(BIcon* icon, BWidget* parent) : BWidget(parent)
{
	member_allocate();
	setIcon(icon);
}
BButton::BButton(BImage* image, BWidget* parent) : BWidget(parent)
{
	member_allocate();
	member.image = image;
}
BButton::BButton(BAction* action, BWidget* parent) : BWidget(parent)
{
	member_allocate();
	setAction(action);
}
BButton::BButton(BMenu* menu, BWidget* parent) : BWidget(parent)
{
	member_allocate();
	setMenu(menu);
}
BButton::~BButton()
{
	member.disconnect(member.shortcut);
	member.disconnect(member.action);
	member.disconnect(member.menu);
	member_release();
}

bool BButton::setIcon(BIcon* icon)
{
	if (BWidget::setIcon(icon))
	{
		member.dirty = true;
		if (member.imageShown())
			this->adjust();
		this->adjust();
		return true;
	}
	return false;
}

void BButton::setAlign(Align align)
{
	if (member.alignment.ia_align != align)
	{
		member.alignment.ia_align = align;
		member.dirty = true;
		if (this->checks(Signal_AlignChanged))
			this->emit(Signal_AlignChanged, align);
		this->adjust();
		this->fresh();
	}
}
Align BButton::align() const
{
	return member.alignment.ia_align;
}

void BButton::setSpacing(int spacing)
{
	if (member.alignment.ia_spacing < 0)
		member.alignment.ia_spacing = 0;
	if (member.alignment.ia_spacing > 1000)
		member.alignment.ia_spacing = 1000;
	if (member.alignment.ia_spacing != spacing)
	{
		member.alignment.ia_spacing = spacing;
		member.dirty = true;
		if (this->checks(Signal_SpacingChanged))
			this->emit(Signal_SpacingChanged, spacing);
		this->adjust();
		this->fresh();
	}
}
int BButton::spacing() const
{
	return member.alignment.ia_spacing;
}

void BButton::setForm(Form form)
{
	if (member.alignment.ia_form != form)
	{
		member.alignment.ia_form = form;
		member.dirty = true;
		if (this->checks(Signal_FormChanged))
			this->emit(Signal_FormChanged, form);
		this->adjust();
		this->fresh();
	}
}
Form BButton::form() const
{
	return member.alignment.ia_form;
}

bool BButton::setText(const BString& text)
{
	if (member.text != text)
	{
		member.text = text;
		member.dirty = true;
		member.t_text.clear();
		if (this->checks(Signal_TextChanged))
			this->emit(Signal_TextChanged, text);
		this->adjust();
		this->fresh();
		return true;
	}
	return false;
}
const BString& BButton::text() const
{
	return member.text;
}

void BButton::setText(const BString& text, bool checked)
{
	if (checked)
	{
		if (member.checkedText == text)
			return;
		member.dirty = true;
		member.checkedText = text;
		member.t_text_checked.clear();
		if (this->checks(Signal_TextChanged))
			this->emit(Signal_TextChanged, checked);
	}
	else
	{
		if (member.uncheckText == text)
			return;
		member.dirty = true;
		member.uncheckText = text;
		member.t_text_uncheck.clear();
		if (this->checks(Signal_TextChanged))
			this->emit(Signal_TextChanged, checked);
	}
	if (member.checkable)
	{
		member.dirty = true;
		this->adjust();
		this->fresh();
	}
}
const BString& BButton::text(bool checked) const
{
	if (checked)
	{
		return member.checkedText;
	}
	else
	{
		return member.uncheckText;
	}
}

bool BButton::setColor(BByte r, BByte g, BByte b, BByte a)
{
	return setColor(BColor(r, g, b, a));
}
bool BButton::setColor(const BColor& color)
{
	if (member.color != color)
	{
		member.color = color;
		if (this->checks(Signal_ColorChanged))
			this->emit(Signal_ColorChanged, color);
		this->fresh();
		return true;
	}
	return false;
}
const BColor& BButton::color() const
{
	return member.color;
}

void BButton::setGraph(Graph graph)
{
	if (member.graph != graph)
	{
		member.graph = graph;
		member.dirty = true;
		if (member.imageShown())
			this->adjust();
		if (this->checks(Signal_GraphChanged))
			this->emit(Signal_GraphChanged, graph);
		this->fresh();
	}
}
Graph BButton::graph() const
{
	return member.graph;
}

void BButton::setIconSize(int iconWidth, int iconHeight)
{
	setIconSize(BSize(iconWidth, iconHeight));
}
void BButton::setIconSize(const BSize& iconSize)
{
	if (member.iconSize != iconSize)
	{
		member.iconSize = iconSize;
		member.dirty = true;
		if (member.imageShown())
			this->adjust();
		this->fresh();
	}
}
const BSize& BButton::iconSize() const
{
	return member.iconSize;
}

void BButton::setImage(BImage* image)
{
	if (member.image != image)
	{
		member.image = image;
		member.dirty = true;
		if (this->checks(Signal_ImageChanged))
			this->emit(Signal_ImageChanged, image);
		this->fresh();
	}
}
BImage* BButton::image() const
{
	return member.image;
}

void BButton::setAction(BAction* action)
{
	if (member.action != action)
	{
		if (member.action)
		{
			member.disconnect(member.action);
		}
		member.action = action;
		if (member.action)
		{
			if (member.menu)
				this->setMenu(0);
			member.connect(action, Signal_Enabled, &member_BButton::slotActionAbleChanged);
			member.connect(action, Signal_IconChanged, &member_BButton::slotActionIconChanged);
			member.connect(action, Signal_TextChanged, &member_BButton::slotActionTextChanged);
			member.connect(action, Signal_Checked, &member_BButton::slotActionChecked);
			member.checkable = action->checkable();
			this->setEnabled(action->enabled());
		}
		member.dirty = true;
		member.t_text.clear();
		member.t_text_checked.clear();
		member.t_text_uncheck.clear();
		this->adjust();
	}
}
BAction* BButton::action() const
{
	return member.action;
}

void BButton::setMenu(BMenu* menu)
{
	if (member.menu != menu)
	{
		if (member.menu)
		{
			member.disconnect(member.menu);
			this->removeChild(member.menu);
		}
		member.menu = menu;
		if (member.menu)
		{
			if (member.action)
				this->setAction(0);
			this->addChild(member.menu);
			member.connect(menu, Signal_Hide, &member_BButton::slotMenuHide);
			member.connect(menu, Signal_Enabled, &member_BButton::slotMenuEnabledChanged);
			member.connect(menu, Signal_IconChanged, &member_BButton::slotMenuIconChanged);
			member.connect(menu, Signal_TitleChanged, &member_BButton::slotMenuTitleChanged);
			member.connect(menu, Signal_Styled, &member_BButton::slotMenuStyleChanged);
			this->setEnabled(menu->enabled());
		}
		member.dirty = true;
		member.t_text.clear();
		member.t_text_checked.clear();
		member.t_text_uncheck.clear();
		this->fresh();
	}
}
BMenu* BButton::menu() const
{
	return member.menu;
}

bool BButton::setShortcut(const BString& text, BWidget* widget)
{
	if (member.shortcut.empty())
	{
		member.shortcut = new BShortcut();
		member.connect(member.shortcut, Signal_Triggered, &member_BButton::slotShortcutTriggered);
	}
	member.shortcut->setWidget(widget);
	return member.shortcut->setText(text);
}
bool BButton::setShortcut(const BKeyArray& keys, BWidget* widget)
{
	if (member.shortcut.empty())
	{
		member.shortcut = new BShortcut();
		member.connect(member.shortcut, Signal_Triggered, &member_BButton::slotShortcutTriggered);
	}
	member.shortcut->setWidget(widget);
	return member.shortcut->setKeys(keys);
}

bool BButton::setShortcut(BShortcut* shortcut)
{
	if (member.shortcut != shortcut)
	{
		if (BShortcut* scut = member.shortcut)
		{
			member.disconnect(member.shortcut, Signal_Triggered);
		}
		member.shortcut = shortcut;
		if (BShortcut* scut = member.shortcut)
		{
			member.connect(member.shortcut, Signal_Triggered, &member_BButton::slotShortcutTriggered);
		}
		return true;
	}
	return false;
}
BShortcut* BButton::shortcut()
{
	return member.shortcut;
}

void BButton::setCheckable(bool checkable)
{
	if (member.checkable != checkable)
	{
		member.checkable = checkable;
		member.checked = false;
		if (member.action)
			member.action->setCheckable(checkable);
		this->fresh();
	}
}
bool BButton::checkable() const
{
	return member.checkable;
}

void BButton::setChecked(bool checked)
{
	if (!member.checkable)
		return;
	if (member.checked != checked)
	{
		member.checked = checked;
		emit(Signal_Checked, member.checked);
		if (member.action && member.action->enabled() && member.action->checkable())
		{
			member.action->setChecked(checked);
		}
		this->fresh();
	}
}
bool BButton::checked() const
{
	return member.checked;
}

void BButton::click()
{
	if (this->enabled())
	{
		member.doclick();
		this->emit(Signal_Clicked, this->centerRect().center());
	}
}
void BButton::click(int x, int y)
{
	if (this->enabled())
	{
		member.doclick();
		this->emit(Signal_Clicked, BPoint(x, y));
	}
}
void BButton::click(const BPoint& point)
{
	if (this->enabled())
	{
		member.doclick();
		this->emit(Signal_Clicked, point);
	}
}

const BString& BButton::realTips() const
{
	if (BAction* action = member.action)
	{
		if (member.shortcutVisible && action->shortcut())
		{
			BShortcut* shortcut = action->shortcut();
			member.realTips = action_member(action)->tips;
			member.realTips << '(' << shortcut->text() << ')';
			return member.realTips;
		}
		if (action_member(action)->tips.size())
			return action_member(action)->tips;
	}
	if (BMenu* menu = member.menu)
	{
		const BString& tips = menu->realTips();
		if (tips.size())
			return tips;
	}
	if (BShortcut* shortcut = member.shortcut)
	{
		member.realTips = BWidget::realTips();
		member.realTips << '(' << shortcut->text() << ')';
		return member.realTips;
	}
	return BWidget::realTips();
}

BSize BButton::sizeHint() const
{
	if (member.dirty)
	{
		member.freshRects();
		member.dirty = false;
	}
	BSize csize = member.alignment.ia_hintSize;
	BSize hsize = this->preferSize(csize);
	return hsize;
}

bool BButton::input(const BString& text, const BValue& value)
{
	if (text == "text")
	{
		if (!value.is<BString>())
			return false;
		const BString& text = value;
		this->setText(text);
		return true;
	}
	if (text == "color")
	{
		if (!value.is<BColor>())
			return false;
		const BColor& color = value;
		this->setColor(color);
		return true;
	}
	if (text == "graph")
	{
		if (!value.is<Graph>())
			return false;
		Graph graph = value;
		this->setGraph(graph);
		return true;
	}
	if (text == "image")
	{
		if (!value.is<BImage>())
			return false;
		BImage* image = value;
		this->setImage(image);
		return true;
	}
	if (text == "icon")
	{
		if (!value.is<BIcon>())
			return false;
		BIcon* icon = value;
		this->setIcon(icon);
		return true;
	}
	if (text == "icon-size")
	{
		if (!value.is<BSize>())
			return false;
		const BSize& size = value;
		this->setIconSize(size);
		return true;
	}
	if (text == "spacing")
	{
		if (!value.is<int>())
			return false;
		int spacing = value;
		this->setSpacing(spacing);
		return true;
	}
	if (text == "align")
	{
		if (!value.is<Align>())
			return false;
		Align align = value;
		this->setAlign(align);
		return true;
	}
	if (text == "form")
	{
		if (!value.is<Form>())
			return false;
		Form form = value;
		this->setForm(form);
		return true;
	}
	return BWidget::input(text, value);
}
bool BButton::query(const BString& text, BValue& value) const
{
	if (text == "text")
	{
		value = member.text;
		return true;
	}
	if (text == "graph")
	{
		value = member.graph;
		return true;
	}
	if (text == "color")
	{
		value = member.color;
		return true;
	}
	if (text == "image")
	{
		value = member.image;
		return true;
	}
	if (text == "checkable")
	{
		value = member.checkable;
		return true;
	}
	if (text == "checked")
	{
		value = member.checked;
		return true;
	}
	if (text == "content-rect")
	{
		value = member.alignment.ia_rect;
		return true;
	}
	if (text == "content-size")
	{
		value = member.alignment.ia_rect.size();
		return true;
	}
	if (text == "icon-rect")
	{
		value = member.alignment.ia_iconRect;
		return true;
	}
	if (text == "icon-area")
	{
		value = member.alignment.ia_iconArea;
		return true;
	}
	if (text == "text-rect")
	{
		value = member.alignment.ia_textRect;
		return true;
	}
	if (text == "text-area")
	{
		value = member.alignment.ia_textArea;
		return true;
	}
	if (text == "real-text")
	{
		value.clear();
		if (member.checked && member.checkedText.size())
			value = member.checkedText;
		if (!member.checked && member.uncheckText.size())
			value = member.uncheckText;
		if (value.empty())
			value = member.text;
		return true;
	}
	if (text == "trans-text")
	{
		if (member.t_text.size())
		{
			value = member.t_text;
		}
		else
		{
			value.clear();
			if (member.checked && member.checkedText.size())
				value = member.checkedText;
			if (!member.checked && member.uncheckText.size())
				value = member.uncheckText;
			if (value.empty())
				value = member.text;
		}
		return true;
	}
	return BWidget::query(text, value);
}

void BButton::resizeEvent(const BEvent& event)
{
	member.dirty = true;
	BWidget::resizeEvent(event);
}

void BButton::styleEvent(const BEvent& event)
{
	BWidget::styleEvent(event);
	const BStyle* style = this->realStyle();

	const BValue& imageValue = style->value(Value_Image);
	if (imageValue.valid())
		member.image = (BImage*)imageValue;

	style_member(style)->assign(Value_Text, member.text);
	style_member(style)->assign(Value_Form, member.alignment.ia_form);
	style_member(style)->assign(Value_Align, member.alignment.ia_align);
	style_member(style)->assign(Value_Spacing, member.alignment.ia_spacing);
	style_member(style)->assign(Value_Color, member.color);
	style_member(style)->assign(Value_Graph, member.graph);
	style_member(style)->assign(Value_Icon_Size, member.iconSize);

	if (const BStyle* shortcutStyle = style->annex("shortcut"))
	{
		style_member(shortcutStyle)->assign(Value_Visible, member.shortcutVisible);
	}

	member.dirty = true;
	member.t_text.clear();
	member.t_text_checked.clear();
	member.t_text_uncheck.clear();
	this->adjust();
}
void BButton::freshEvent(const BEvent& event)
{
	if (member.dirty)
	{
		member.freshRects();
		member.dirty = false;
	}
	BWidget::freshEvent(event);
}
void BButton::paintEvent(const BEvent& event)
{
	BPainter painter(this);

	State state = painter.state();
	if (member.checked)
		state = State_Checked;
	if (this->hovered())
	{
		if (member.pressed)
			state |= State_Pressed;
		else
			state |= State_Hovered;
	}

	painter.drawBackground(state);
	painter.drawForeground(state);

	BRect crect = this->centerRect();
	if (member.alignment.ia_iconRect.valid())
	{
		BRect iconRect = member.alignment.ia_iconRect;
		if (member.iconSize.empty())
			iconRect = crect.wrap(iconRect);
		BImage* image = member.realImage();
		if (BIcon* icon = member.realIcon())
		{
			image = icon->image(state);
		}
		if (!painter.setColor(member.color))
		{
			painter.setColor(Color_Text, state);
		}
		if (image)
		{
			painter.drawImage(iconRect, image, member.iconSize.empty());
			event.accept();
		}
		else if (member.graph > Graph_None && member.graph < Graph_Max)
		{
			painter.drawGraph(iconRect, member.graph);
			event.accept();
		}
	}

	if (member.alignment.ia_textRect.valid())
	{
		bool drawn = false;
		painter.setColor(Color_Text, state);
		if (member.checkable)
		{
			if (member.checked && member.checkedText.size())
			{
				if (member.t_text_checked.size())
					painter.drawText(member.alignment.ia_textRect, member.t_text_checked, member.alignment.ia_align);
				else
					painter.drawText(member.alignment.ia_textRect, member.checkedText, member.alignment.ia_align);
				drawn = true;
			}
			if (!member.checked && member.uncheckText.size())
			{
				if (member.t_text_uncheck.size())
					painter.drawText(member.alignment.ia_textRect, member.t_text_uncheck, member.alignment.ia_align);
				else
					painter.drawText(member.alignment.ia_textRect, member.uncheckText, member.alignment.ia_align);
				drawn = true;
			}
		}
		if (!drawn)
		{
			if (member.t_text.size())
				painter.drawText(member.alignment.ia_textRect, member.t_text, member.alignment.ia_align);
			else if (member.text.size())
				painter.drawText(member.alignment.ia_textRect, member.text, member.alignment.ia_align);
			else if (member.action)
				painter.drawText(member.alignment.ia_textRect, member.action->text(), member.alignment.ia_align);
		}
	}

	painter.drawBorder(state);
	painter.drawPerch(state);
	painter.drawMargin(state);
}

void BButton::hoverEvent(const BEvent& event)
{
	this->fresh();
	BWidget::hoverEvent(event);
}

void BButton::keyPressEvent(const BKeyEvent& keyEvent)
{
	if (keyEvent.key() == Key_Enter)
	{
		this->click();
		keyEvent.accept();
	}
	BWidget::keyPressEvent(keyEvent);
}
void BButton::keyReleaseEvent(const BKeyEvent& keyEvent)
{
	BWidget::keyReleaseEvent(keyEvent);
}

void BButton::mousePressEvent(const BMouseEvent& mouseEvent)
{
	BWidget::mousePressEvent(mouseEvent);
	if (mouseEvent.button() == Button_Left)
	{
		member.pressed = true;
		this->fresh();
	}
}
void BButton::mouseReleaseEvent(const BMouseEvent& mouseEvent)
{
	BWidget::mouseReleaseEvent(mouseEvent);
	if (mouseEvent.button() == Button_Left)
	{
		member.pressed = false;
		this->fresh();
	}
}

void BButton::mouseClickEvent(const BMouseEvent& mouseEvent)
{
	const BPoint& pos = mouseEvent.pos();
	if (mouseEvent.button() == Button_Left)
	{
		member.doclick();
	}
	BWidget::mouseClickEvent(mouseEvent);
}
